#include <chrono>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <vector>

class AsyncLog
{
    public:
    AsyncLog(const std::string& filename) : m_ofs(filename, std::ios::app), m_isRuning(true)
    {
        m_thread = std::thread([this]() { this->fulshLog(); });
    }

    ~AsyncLog()
    {
        {
            std::unique_lock lc(m_mutex);
            m_isRuning = false;
            m_condition.notify_all();
        }

        if (m_thread.joinable())
        {
            m_thread.join();
        }
    }

    void Log(const std::string& message)
    {
        std::unique_lock lk(m_mutex);
        m_curBuf.push_back(message);
        if (m_curBuf.size() > 100)
        {
            m_Bufs.push_back(std::move(m_curBuf));
            m_curBuf.clear();
            m_condition.notify_all();
        }
    }

    private:
    void fulshLog()
    {
        std::vector<std::string> buf;
        while (true)
        {
            std::unique_lock lc(m_mutex);
            m_condition.wait_for(lc, std::chrono::seconds(2), [this]() { return !m_isRuning || !m_Bufs.empty(); });

            if (!m_isRuning && m_Bufs.empty() && m_curBuf.empty())
            {
                return;
            }

            if (!m_Bufs.empty())
            {
                buf = (std::move(m_Bufs.front()));
                m_Bufs.erase(m_Bufs.begin());
            }
            else if (!m_curBuf.empty())
            {
                buf = std::move(m_curBuf);
                m_curBuf.clear();
            }

            for (const auto& message : buf)
            {
                m_ofs << message << "\n";
            }
            m_ofs.flush();
        }
    }

    private:
    std::ofstream m_ofs;
    bool m_isRuning = false;
    std::vector<std::string> m_curBuf;
    std::vector<std::vector<std::string>> m_Bufs;
    std::mutex m_mutex;
    std::condition_variable m_condition;
    std::thread m_thread;
};

int main(void)
{

    AsyncLog log("test_async.log");
    auto time = std::chrono::steady_clock::now();
    log.Log(std::to_string(1));
    // for (int i = 0; i < 10000; i++)
    // {
    //     log.Log(std::to_string(i));
    // }

    auto now = std::chrono::steady_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::microseconds>(now - time).count();
    return 0;
}